﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace SimpleRedisClinet
{
    public enum ResponseCommandType
    {
        ErrorRet,
        SuccessMsgRet,
        NumberRet,
        StringsRet
    };

    /// <summary>
    /// redis server response data class
    /// yangmeng(hvbird@gmail.com)
    /// 2011-01-23
    /// </summary>
    internal class ResponseClass
    {
        private ResponseCommandType m_responsetype;
        private int m_Number;
        private List<string> m_strings;
        private Socket m_sock;

        public ResponseClass(Socket sock)
        {
            m_strings = new List<string>();
            m_sock = sock;
        }

        public void ReceiveData()
        {
            byte[] bytes = new byte[1];
            int datalen = m_sock.Receive(bytes, 1, SocketFlags.None);

            if (datalen <= 0)
            {
                m_responsetype = ResponseCommandType.ErrorRet;
                return;
            }

            char tmpchar = (char)bytes[0];

            switch (tmpchar)
            {
                case ':':
                    {
                        m_responsetype = ResponseCommandType.NumberRet;

                        string number = receive_line();
                        if (number.Length == 0)
                        {
                            m_responsetype = ResponseCommandType.ErrorRet;
                            return;
                        }

                        m_Number = Convert.ToInt32(number);
                    }
                    break;
                case '+':
                    {
                        m_responsetype = ResponseCommandType.SuccessMsgRet;
                        m_strings.Add(receive_line());
                    }
                    break;
                case '$':
                    {
                        m_responsetype = ResponseCommandType.StringsRet;

                        string number = receive_line();
                        if (number.Length == 0)
                        {
                            return;
                        }

                        m_strings.Add(receive_string_haslen(Convert.ToInt32(number)));
                    }
                    break;
                case '*':
                    {
                        m_responsetype = ResponseCommandType.StringsRet;

                        string number = receive_line();
                        if (number.Length == 0)
                            return;

                        int stringcount = Convert.ToInt32(number);
                        while (m_strings.Count < stringcount)
                        {
                            datalen = m_sock.Receive(bytes, 1, SocketFlags.None);

                            if (datalen <= 0 || bytes[0] != '$')
                                return;

                            number = receive_line();
                            if (number.Length == 0)
                            {
                                m_responsetype = ResponseCommandType.ErrorRet;
                                return;
                            }

                            m_strings.Add(receive_string_haslen(Convert.ToInt32(number)));
                        }
                    }
                    break;
                default:
                    m_responsetype = ResponseCommandType.ErrorRet;
                    break;
            }
        }

        private string receive_string_haslen(int len)
        {
            byte[] bytes = new byte[len+2];

            int dataleft = len+2;

            while (dataleft > 0)
            {
                int datalen = m_sock.Receive(bytes, bytes.Length - dataleft, dataleft, SocketFlags.None);

                if (datalen <= 0)
                    break;

                dataleft -= datalen;
            }

            return Encoding.UTF8.GetString(bytes, 0, len);
        }

        private string receive_line()
        {
            MemoryStream stream = new MemoryStream();

            byte[] bytes = new byte[1];
            int datalen = m_sock.Receive(bytes, 1, SocketFlags.None);

            string retstr = "";
            if (datalen <= 0)
            {
                m_responsetype = ResponseCommandType.ErrorRet;
                return retstr;
            }

            while (datalen > 0)
            {
                stream.Write(bytes, 0, 1);

                if (bytes[0] == '\n')
                    break;

                datalen = m_sock.Receive(bytes, 1, SocketFlags.None);
            }

            if (stream.Length > 0)
            {
                bytes = new byte[stream.Length-2];
                stream.Position = 0;
                stream.Read(bytes,0,bytes.Length);
                retstr = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            }

            return retstr;
        }

        public ResponseCommandType ResponseType
        {
            get { return m_responsetype; }
        }

        public int Number
        {
            get { return m_Number; }
        }

        public List<string> Strings
        {
            get { return m_strings; }
        }
    }
}
